<?php

/*

    MMetaverse - Open Metaverse Access Library

    Copyright (c) 2009 Mary Jane Soft - Marlon J. Manrique

    http://www.maryjanesoft.com
    http://www.marlonj.com

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General
    Public License along with this library; if not, write to the
    Free Software Foundation, Inc., 59 Temple Place, Suite 330,
    Boston, MA  02111-1307  USA

    $Id$

*/

/**
 * @package mjs.mmetaverse.llsd
 */

/**
 * Encode an array like a llsd xml document
 */
class MLLSDEncoder {

    /**
     * XML Document 
     *
     * @var DOMDocument
     */
    private $document;

    /**
     * Document root node 
     *
     * @var DOMElement
     */
    private $root;

    /**
     * Create an empty encoder without data 
     */
    private function MLLSDEncoder() {
        // Create an empty document
        $this->document = new DOMDocument("1.0");

        // Add the root node like llsd 
        $this->root = $this->document->createElement("llsd");

        // Add the root to the document 
        $this->document->appendChild($this->root);
    }

    /**
     * Encode the array into a llsd document
     *
     * @param array $data The array representation of the llsd 
     */
    private function encodeData(array $data) {
        // Create a node with the array information
        $node = $this->encodeNode($data);
        // Append the the node 
        $this->root->appendChild($node);
    }

    /**
     * Create a node acoording the data type
     *
     * @param mixed $data
     * @return DOMNode
     */
    private function encodeNode($data) {
        // Encode arrays 
        if(is_array($data)) {
            // Check if the array is a map or an array 
            return $this->checkArray($data);
        }
        // Encode integer values
        else if(is_int($data)) {
            return $this->encodeInteger($data);
        }
        // Encode float values 
        else if(is_float($data)) {
            return $this->encodeReal($data);
        }
        // Encode uuids
        else if($data instanceof MUUID) {
            return $this->encodeUUID($data);
        }
        // If no type, encode like string
        else {
            return $this->encodeString($data);
        }
    }

    /**
     * Encode the array like a map or array according index 
     *
     * @param mixed $data
     * @return DOMNode
     */
    private function checkArray($data) {
        // Check if the first index is zero
        // is array 
        if(array_key_exists(0,$data)) {
            return $this->encodeArray($data);
        }
        // First index is not a number, is map 
        else {
            return $this->encodeMap($data);
        }
    }

    /**
     * Encode a map with the array
     *
     * @param array $data
     * @return DOMNode
     */
    private function encodeMap(array $data) {
        // Create the map element 
        $element = $this->document->createElement("map");

        // Add each element like a pair 
        foreach($data as $key => $value)
        {
            // Create the key
            $keyElement = $this->document->createElement("key");
            $text = $this->document->createTextNode(utf8_encode($key));
            $keyElement->appendChild($text);

            // Create the value with the value of position 
            $valueElement = $this->encodeNode($value);
            $element->appendChild($keyElement);
            $element->appendChild($valueElement);
        }

        // Return the element
        return $element;
    }

    /**
     * Encode the array like an array
     *
     * @param array $data
     * @return DOMNode
     */
    private function encodeArray(array $data) {
        // Create the element 
        $element = $this->document->createElement("array");

        // Just add the values 
        foreach($data as $node)
        {
            $value = $this->encodeNode($node);
            $element->appendChild($value);
        }

        // Return the element 
        return $element;
    }

    /**
     * Encode like integer 
     * @param integer $data
     * @return DOMNode
     */
    private function encodeInteger($data) {
        $element = $this->document->createElement("integer");
        $text = $this->document->createTextNode($data);
        $element->appendChild($text);
        return $element;
    }

    /**
     * Encode like float
     * @param float $data
     * @return DOMNode
     */
    private function encodeReal($data) {
        $element = $this->document->createElement("real");
        $text = $this->document->createTextNode($data);
        $element->appendChild($text);
        return $element;
    }

    /**
     * Encode like uuid
     * @param MUUID $data
     * @return DOMNode
     */
    private function encodeUUID(MUUID $data) {
        $element = $this->document->createElement("uuid");
        $text = $this->document->createTextNode($data->getHyphenated());
        $element->appendChild($text);
        return $element;
    }

    /**
     * Encode like string
     * @param string $data
     * @return DOMNode
     */
    private function encodeString($data) {
        $element = $this->document->createElement("string");
        $text = $this->document->createTextNode(utf8_encode($data));
        $element->appendChild($text);
        return $element;
    }

    /**
     * Return the xml of the llsd
     *
     * @return string 
     */
    public function getXML() {
        return $this->document->saveXML();
    }

    /**
     * Convert array into llsd 
     *
     * @param array $data
     * @return string
     */
    public static function encode(array $data) {
        $llsd = new MLLSDEncoder();
        $llsd->encodeData($data);
        return $llsd->getXML();
    }
}

?>
